package com.tsing.wms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWT;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tsing.wms.config.RedisManager;
import com.tsing.wms.constant.AuthConsts;
import com.tsing.wms.dto.UserDTO;
import com.tsing.wms.entity.*;
import com.tsing.wms.exception.CommonException;
import com.tsing.wms.exception.ServerException;
import com.tsing.wms.mapper.*;
import com.tsing.wms.service.IUserService;
import com.tsing.wms.util.GetLoginUserUtil;
import com.tsing.wms.util.MD5Util;
import com.tsing.wms.vo.PageResultVO;
import com.tsing.wms.vo.req.*;
import com.tsing.wms.vo.resp.LoginRespVO;
import com.tsing.wms.vo.resp.UserRespVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.time.Instant;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author TheTsing
 * @since 2022-04-13
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements IUserService {

    @Value("${frame.tokenExpire.time:3600}")
    private Long tokenExpireTime;

    @Autowired
    private RedisManager redisManager;

    @Override
    public LoginRespVO login(LoginReqVO reqVO) {
        UserEntity userEntity = new LambdaQueryChainWrapper<>(baseMapper)
                .select(UserEntity::getId, UserEntity::getPassword, UserEntity::getIdentity)
                .eq(UserEntity::getPhone, reqVO.getPhone())
                .one();
        if (ObjectUtil.isNull(userEntity)) {
            throw new ServerException(CommonException.Proxy.USER_IS_NULL);
        }
        if (!MD5Util.verifyPassword(reqVO.getPassword(), userEntity.getPassword(), AuthConsts.PASSWORD_MD5_COUNT)) {
            throw new ServerException(CommonException.Proxy.PASSWORD_ERROR);
        }
        String token = getToken(userEntity);
        return new LoginRespVO().setId(userEntity.getId()).setToken(token);
    }

    private String getToken(UserEntity userEntity) {
        //限制账号同时只能在一处登录
        String token = redisManager.getString(AuthConsts.REDIS_TOKEN_PREFIX + userEntity.getId());
        if (StrUtil.isNotBlank(token)) {
            redisManager.deleteString(token);
        }
        UserDTO userDTO = new UserDTO()
                .setId(userEntity.getId())
                .setIdentity(userEntity.getIdentity())
                .setToken(JWT
                        .create()
                        .setPayload(IdUtil.fastSimpleUUID(), userEntity.getId() + Instant.now().toEpochMilli())
                        .setKey(userEntity.getId().getBytes(StandardCharsets.UTF_8))
                        .sign())
                .setExpire(tokenExpireTime);
        redisManager.setString(userDTO.getToken(), JSONObject.toJSONString(userDTO), userDTO.getExpire());
        redisManager.setString(AuthConsts.REDIS_TOKEN_PREFIX + userDTO.getId(), userDTO.getToken(), userDTO.getExpire());
        return userDTO.getToken();
    }


    @Override
    public void addUser(UserReqVO reqVO, HttpServletRequest request) {
        if (GetLoginUserUtil.getLoginUserInfo(request.getHeader(AuthConsts.AUTHENTICATION_HEAD), redisManager).getIdentity() != 1) {
            throw new ServerException(CommonException.Proxy.AUTHORIZATION_EXCEPTION);
        }
        if (ObjectUtil.isNotNull(new LambdaQueryChainWrapper<>(baseMapper)
                .select(UserEntity::getId)
                .eq(UserEntity::getPhone, reqVO.getPhone())
                .one())) {
            throw new ServerException(CommonException.Proxy.PHONE_IS_NOTNULL);
        }
        String pwd = MD5Util.getMD5ByStr(reqVO.getPhone().substring(reqVO.getPhone().length() - 6), AuthConsts.PASSWORD_MD5_COUNT);
        UserEntity userEntity = new UserEntity().setPassword(pwd);
        BeanUtil.copyProperties(reqVO, userEntity);
        baseMapper.insert(userEntity);
    }

    @Override
    public void updateUser(UserReqVO reqVO, HttpServletRequest request) {
        if (GetLoginUserUtil.getLoginUserInfo(request.getHeader(AuthConsts.AUTHENTICATION_HEAD), redisManager).getIdentity() != 1) {
            throw new ServerException(CommonException.Proxy.AUTHORIZATION_EXCEPTION);
        }
        if (StrUtil.isNotBlank(reqVO.getPhone())) {
            if (ObjectUtil.isNotNull(new LambdaQueryChainWrapper<>(baseMapper)
                    .select(UserEntity::getId)
                    .eq(UserEntity::getPhone, reqVO.getPhone())
                    .ne(UserEntity::getId, reqVO.getId())
                    .one())) {
                throw new ServerException(CommonException.Proxy.PHONE_IS_NOTNULL);
            }
        }
        if (ObjectUtil.isNotNull(reqVO.getIdentity()) && (reqVO.getIdentity() == 1 || reqVO.getIdentity() == 2)) {
            String token = redisManager.getString(AuthConsts.REDIS_TOKEN_PREFIX + reqVO.getId());
            if (StrUtil.isNotBlank(token)) {
                UserDTO userDTO = JSONObject.parseObject(redisManager.getString(token), UserDTO.class);
                userDTO.setIdentity(reqVO.getIdentity());
                redisManager.setString(token, JSONObject.toJSONString(userDTO), userDTO.getExpire());
            }
        }
        UserEntity userEntity = new UserEntity();
        BeanUtil.copyProperties(reqVO, userEntity);
        baseMapper.updateById(userEntity);
    }

    @Override
    public void updatePwd(PwdUpdateReqVO reqVO, HttpServletRequest request) {
        String id = GetLoginUserUtil.getLoginUserInfo(request.getHeader(AuthConsts.AUTHENTICATION_HEAD), redisManager).getId();
        UserEntity userEntity = new LambdaQueryChainWrapper<>(baseMapper)
                .select(UserEntity::getId, UserEntity::getPassword)
                .eq(UserEntity::getId, id)
                .one();
        if (!MD5Util.verifyPassword(reqVO.getOldPassword(), userEntity.getPassword(), AuthConsts.PASSWORD_MD5_COUNT)) {
            throw new ServerException(CommonException.Proxy.PASSWORD_ERROR);
        }
        new LambdaUpdateChainWrapper<>(baseMapper)
                .set(UserEntity::getPassword, MD5Util.getMD5ByStr(reqVO.getNewPassword(), AuthConsts.PASSWORD_MD5_COUNT))
                .eq(UserEntity::getId, id)
                .update();
    }

    @Override
    public void initPwd(IdReqVO reqVO, HttpServletRequest request) {
        if (GetLoginUserUtil.getLoginUserInfo(request.getHeader(AuthConsts.AUTHENTICATION_HEAD), redisManager).getIdentity() != 1) {
            throw new ServerException(CommonException.Proxy.AUTHORIZATION_EXCEPTION);
        }
        UserEntity userEntity = new LambdaQueryChainWrapper<>(baseMapper)
                .select(UserEntity::getId, UserEntity::getPhone)
                .eq(UserEntity::getId, reqVO.getId())
                .one();
        String pwd = MD5Util.getMD5ByStr(userEntity.getPhone().substring(userEntity.getPhone().length() - 6), AuthConsts.PASSWORD_MD5_COUNT);
        userEntity.setPassword(pwd);
        baseMapper.updateById(userEntity);
    }

    @Override
    public UserRespVO getUser(IdReqVO reqVO) {
        UserEntity userEntity = baseMapper.selectById(reqVO.getId());
        UserRespVO userRespVO = new UserRespVO();
        BeanUtil.copyProperties(userEntity, userRespVO);
        return userRespVO;
    }

    @Override
    public PageResultVO<UserRespVO> userPage(UserPageReqVO reqVO) {
        IPage<UserEntity> page = new Page<>(reqVO.getPageNum(), reqVO.getPageSize());
        new LambdaQueryChainWrapper<>(baseMapper)
                .like(StrUtil.isNotBlank(reqVO.getUsername()), UserEntity::getUsername, reqVO.getUsername())
                .like(StrUtil.isNotBlank(reqVO.getPhone()), UserEntity::getPhone, reqVO.getPhone())
                .page(page);
        return new PageResultVO<>(page, BeanUtil.copyToList(page.getRecords(), UserRespVO.class));
    }

    @Resource
    private SupplierMapper supplierMapper;
    @Resource
    private StorehouseMapper storehouseMapper;
    @Resource
    private OutInRecordMapper outInRecordMapper;
    @Resource
    private OrderMapper orderMapper;

    @Override
    public void deleteUser(IdsReqVO reqVO, HttpServletRequest request) {
        if (GetLoginUserUtil.getLoginUserInfo(request.getHeader(AuthConsts.AUTHENTICATION_HEAD), redisManager).getIdentity() != 1) {
            throw new ServerException(CommonException.Proxy.AUTHORIZATION_EXCEPTION);
        }
        for (String id : reqVO.getIds()) {
            if (0 != new LambdaQueryChainWrapper<>(supplierMapper)
                    .select(SupplierEntity::getId)
                    .eq(SupplierEntity::getUserId, id)
                    .count()) {
                throw new ServerException(CommonException.Proxy.USER_RELEVANCE_SUPPLIER);
            }
            if (0 != new LambdaQueryChainWrapper<>(storehouseMapper)
                    .select(StorehouseEntity::getId)
                    .eq(StorehouseEntity::getUserId, id)
                    .count()) {
                throw new ServerException(CommonException.Proxy.USER_RELEVANCE_STOREHOUSE);
            }
            if (0 != new LambdaQueryChainWrapper<>(outInRecordMapper)
                    .select(OutInRecordEntity::getId)
                    .eq(OutInRecordEntity::getUserId, id)
                    .count()) {
                throw new ServerException(CommonException.Proxy.USER_RELEVANCE_OUT_IN_RECORD);
            }
            if (0 != new LambdaQueryChainWrapper<>(orderMapper)
                    .select(OrderEntity::getId)
                    .eq(OrderEntity::getUserId, id)
                    .count()) {
                throw new ServerException(CommonException.Proxy.USER_RELEVANCE_ORDER);
            }
        }
        for (String id : reqVO.getIds()) {
            String token = redisManager.getString(AuthConsts.REDIS_TOKEN_PREFIX + id);
            if (StrUtil.isNotBlank(token)) {
                redisManager.deleteString(token);
                redisManager.deleteString(AuthConsts.REDIS_TOKEN_PREFIX + id);
            }
        }
        baseMapper.deleteBatchIds(reqVO.getIds());
    }

}
